#ifndef VIA_VIAWIN_H__
#define VIA_VIAWIN_H__

#include <windows.h>
#include <tchar.h>
#include <process.h>

//#include "commands.h"
#include "viaimg.h"

#ifdef COMMANDS_EXPORTS
#define VIACMD_API __declspec(dllexport)
#else
#define VIACMD_API __declspec(dllimport)
#endif

typedef enum VIA_DATATYPE {
	VIA_CHAR,
	VIA_SHORT,
	VIA_INT,
	VIA_LONG,
	VIA_BYTE, 
	VIA_WORD,
	VIA_DWORD,
	VIA_FLOAT,
	VIA_DOUBLE,
	VIA_PVOID
} VIA_DATATYPE;

#ifdef __cplusplus
extern "C" {
#endif


	VIACMD_API	void	viaCommandsRelease(HANDLE *pCmds);
	VIACMD_API	BOOL	viaCommandsEvaluateStr(HANDLE pCmds, char* expression, TCHAR* value, int size);
	VIACMD_API	BOOL	viaCommandsEvaluate(HANDLE pCmds, char* expression, PVOID value, VIA_DATATYPE type);

	VIACMD_API	BOOL	viaCommandsReadMemory(HANDLE pCmds, PVOID pDst, PVOID pSrc, int size);
	//VIACMD_API	BOOL	viaCommandsWriteMemory(HANDLE pCmds, PVOID pDst, PVOID pSrc, int size);

	VIACMD_API	LONG	viaCommandsGetVSMainWnd(HANDLE pCmds);
	//VIACMD_API	void	viaCommandsUpdateDebugger(HANDLE pCmds);

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
extern "C" {
#endif
	
	LRESULT CALLBACK CViaWin_WndProc (HWND, UINT, WPARAM, LPARAM) ;
	LRESULT CALLBACK CViaWin_CtlProc (HWND, UINT, WPARAM, LPARAM) ;
	LRESULT CALLBACK CViaWin_MsgWndProc (HWND, UINT, WPARAM, LPARAM) ;
	LRESULT CALLBACK CViaWin_VsWndProc (HWND, UINT, WPARAM, LPARAM) ;
	DWORD WINAPI viaDialog_Thread(PVOID pParm);
	DWORD WINAPI viaMsgWnd_Thread(PVOID pParm);
	BOOL CALLBACK CViaWin_DlgProc(HWND, UINT, WPARAM, LPARAM);
	
#ifdef __cplusplus
}
#endif

namespace nVIA {
	class CGui;
	class CWin;
	class CDialog;
	class CData;

// 	CData**	GetTlsDataEx();
// 	CData**	GetTlsData();
}

/********************************************************************
 * CData
 * Responsible for return value or data to CViaWin_WndProc according 
 * to user's input.
 * Related to thread local storage index g_dwViaTlsIndex and 
 * used by CViaWin_WndProc.
 ********************************************************************/

class nVIA::CData {
public:
	CData() { reset(); }
	CData(HANDLE hViaCommands) { 
		reset();
		m_pBitmap = NULL;
		Init(hViaCommands); 
	}
	~CData() {Release();}

	//void	SetViaCommands(HANDLE hViaCommands);
	BOOL	Init(HANDLE hViaCommands) {
		reset();
		Release();
		m_hViaCommands = hViaCommands;
		return TRUE;
	}
	void	Release() {
		if (m_pBitmap) {
			delete m_pBitmap;
			m_pBitmap = NULL;
		}
	}
	BOOL	SetWidth(char* expression) {
		return getValue(expression, &m_width);
	}
	BOOL	SetHeight(char* expression) {
		return getValue(expression, &m_height);
	}
	BOOL	SetBits(char* expression) {
		return getPointer(expression, &m_bits);
	}
	void	EnableAlign() { m_isAlign = TRUE; }
	void	EnableAlign(BOOL enable) { m_isAlign = enable; }
	BOOL	IsAlign() { return m_isAlign; }
	void	SetFormatIndex(int index) { m_formatIndex = index; }
	int		GetWidth() {return m_width;}
	int		GetHeight() {return m_height;}
	int		GetPitch() {
		if (m_bHasImage)
			return m_pBitmap->Pitch();
		else
			return 0;
	}
	int		GetFormatIndex() { return m_formatIndex; }
	PBYTE	GetBits() {
		if (m_bHasImage)
			return m_pBitmap->Bits();
		return NULL;
	}
	BITMAPINFO*	GetInfo() {
		if (m_bHasImage)
			return m_pBitmap->Info();
		return NULL;
	}

	BOOL	UpdateData();
	//void	SetWin(nVIA::CWin* pwin) { m_pWin = pwin; }
	//CWin*	GetWin() { return m_pWin; }

protected:
	BOOL	getValue(char* expression, long* value);
	BOOL	getPointer(char* expression, PBYTE* pt);
	void	reset() {
		m_width = 0;
		m_height = 0;
		m_bits = NULL;
		m_bHasImage = FALSE;
		m_formatIndex = ColorFormat_BYTE;
		m_isAlign = TRUE;
	}

protected:
	HANDLE			m_hViaCommands;
	nVIA::CBitmap*	m_pBitmap;
	//nVIA::CWin*		m_pWin;

private:
	long	m_width;
	long	m_height;
	int		m_formatIndex;
	BYTE*	m_bits;
	BOOL	m_bHasImage;
	BOOL	m_isAlign;
}; 

/******************************
 * CGui
 * Manage windows,
 ******************************/

#define VIA_DIALOG_MAX_NUM 16

class nVIA::CGui {
public:
	CGui(HINSTANCE hInstance, HANDLE hViaCommands, bool useDialog = false);
	~CGui();

	int		Create(HINSTANCE hInstance, HANDLE hViaCommands);
	void	Release();
	int		Dialog();
	
	TCHAR*		GetAppName() const { return m_szAppName; }
	TCHAR*		GetClassName(bool isMsgWnd = false) const {
		if (isMsgWnd)
			return m_msgWndClassName;
		else
			return m_szAppName;
	}
	HINSTANCE	GetInstance() const {return m_hInstance;}
	HANDLE		GetViaCommands() const { return m_hViaCommands; }
	HWND		GetVSMainWnd() const { return (HWND)viaCommandsGetVSMainWnd(m_hViaCommands); }
	
private:
	ATOM	RegisterClass(bool isMsgWnd = false);

protected:
	HINSTANCE		m_hInstance;
	WNDCLASSEX		m_wndclass;
	WNDCLASSEX		m_msgWndClass;
	
	HANDLE			m_hViaCommands;	// pointer to vs/ds application
	nVIA::CWin*		m_lstWin[VIA_DIALOG_MAX_NUM];	// TODO replace with std::list
	int				m_nWin;
	ATOM			m_WndClass;

	bool			m_useDialog;

protected:
	static TCHAR*	m_szAppName;
	static WNDPROC	m_wndProc;
	static TCHAR*	m_msgWndClassName;
	static WNDPROC	m_msgWndProc;

public:
	static WNDPROC	m_vsWndProc;
};


/******************************
 * CWin
 * Display VC Image Assist GUI
 ******************************/
#define WM_VIA_UPDATE	WM_USER+1

#define INIT_WND_WIDTH	620
#define INIT_WND_HEIGHT	450

#define CHILD_WND_NUM	(10)
#define	EDIT_LINE_BUFFER_SIZE	(1024)
#define TAB_STOP_CTRL_NUM		(6)	
#define EDIT_NUM		(3)

/* Don't edit their value. It's magic. */
#define ID_STATIC_TOOLBOX	(9)
#define ID_STATIC_WIDTH		(8)
#define ID_STATIC_HEIGHT	(7)
#define ID_STATIC_DATA		(6)
//#define ID_STATIC_INFO		(10)
#define ID_BUTTON_ALIGN		(5)
#define ID_EDIT_DATA		(0)
#define	ID_EDIT_WIDTH		(1)
#define	ID_EDIT_HEIGHT		(2)
#define	ID_LIST_FORMAT		(3)
#define	ID_BUTTON_OK		(4)

class nVIA::CWin {
public:
	//CWin();
	CWin(CGui* pGui, bool toCreate = false);
	//CWin(szAppName, char* szPrjName);
	virtual ~CWin() {Release();}

	virtual int		Create(bool isMsgWnd = false);
	//int		Create(char* szAppName, char* szPrjName);
	virtual void	Run();
	void	Release();
	//todo how to implement it
	//void	CloseThread();

	HANDLE	GetCommands() const { return m_pGui->GetViaCommands(); }
	HWND	GetWnd() const { return m_hWnd; }
	HWND	GetWnd(int i) const { return m_hChildWnds[i]; }
	HWND	GetMsgWnd() const { return m_hMsgWnd; }
	TCHAR*	GetAppName() const { return m_pGui->GetAppName(); }
	HINSTANCE	GetInstance() const { return m_pGui->GetInstance(); }
	CData*	GetData() { return m_pData; }
	BOOL	IsDestoyed() { 
		if (!m_hWnd) 
			return TRUE; 
		return FALSE;
	}
	virtual bool	IsDialog() {return m_isDialog; }

public:
	void	GetMetric();
	void	GetChildPos(RECT* prt);
	void	DestroyWnd();
	BOOL	UpdateData();
	int		GetFocus() const { return m_nFocus; }
	void	SetFocus(int fc) { m_nFocus = fc; }
	int		GetCtlNum() const { return TAB_STOP_CTRL_NUM; }
	WNDPROC	GetOldWndProc(int i) const { 
		return m_ctrlWndProcs[i]; 
	}

	BOOL	OnCreate(HWND hWnd);
	BOOL	OnSize(RECT* prt);
	BOOL	OnToolbar(int nWndId, int nNotify, HWND hWnd);
	BOOL	OnLButtonDown(int nNotify, int x, int y);
	BOOL	OnPaint();
	BOOL	OnDestroy();

public:
	ULONG	m_threadID;
	HANDLE	m_thread;
	HANDLE	m_threadMsg;
	ULONG m_threadIdMsg;

protected:
	int		_Create2();
	void	_SetWnd(HWND wnd) { m_hWnd = wnd; }

protected:
	const nVIA::CGui*	m_pGui;
	HWND	m_hWnd;
	CData*	m_pData;
	bool	m_isDialog;

	HWND	m_hMsgWnd;

	// toolbox
	HWND	m_hChildWnds[CHILD_WND_NUM];
	int		m_toolbarHeight;
	int		m_ctrlHeight;
	int		m_childWndWidths[CHILD_WND_NUM];
	POINT	m_childWndPoses[CHILD_WND_NUM];
	TCHAR	m_editContents[EDIT_NUM][EDIT_LINE_BUFFER_SIZE];
	int		m_editDirties[EDIT_NUM];
	WNDPROC m_ctrlWndProcs[TAB_STOP_CTRL_NUM];
	int		m_nFocus;

	// image
	BOOL	m_bDataDirty;
	BOOL	m_bSync;
};

class nVIA::CDialog : public nVIA::CWin {
public:
	CDialog(CGui* pGui);
	virtual ~CDialog();

	virtual int Create();
	virtual void Run();
};

#endif//VIA_VIAWIN_H__